/**
 *	Copyright (c) 2009, SpLib.net
 * 	All rights reserved.
  	
 * 	Author:6spring
 * 	About:远程过程调用
*/


package SpLib.Net
{
	import SpLib.Com.SingletonFactory;
	import SpLib.Com.SpLibEvent;
	import SpLib.Tools.Tr;
	import SpLib.std.iterator;
	import SpLib.std.list;
	
	import flash.utils.getTimer;
	
	/**
	 * RPC 远程过程调用
	*/
	public class RPC
	{
		public static const DEF_OUTTIME:int	= 30*1000;
		
		public static const NMERR_NONETNODE_NO:int		= -1;		//未找到网络节点
		public static const NMERR_NONETNODE_MSG:String	= "未找到网络节点";
		
		public static const NMERR_NOSERVICE_NO:int		= -2;		//未找到服务
		public static const NMERR_NOSERVICE_MSG:String	= "服务不存在";
		
		public static const NMERR_NOGROUP_NO:int		= -3;		//未找到组
		public static const NMERR_NOGROUP_MSG:String	= "错误的网络组";
		
		public static const NMERR_NONETOBJ_NO:int		= -4;		//未找到目标Obj
		public static const NMERR_NONETOBJ_MSG:String	= "未找到目标Obj";
		
		public static const NMERR_NOFUN_NO:int			= -5;		//未找到函数
		public static const NMERR_NOFUN_MSG:String		= "未找到函数";
		
		public static const NMERR_TIMEOUT_NO:int		= -6;		//调用超时
		public static const NMERR_TIMEOUT_MSG:String	= "调用超时";
		
		public static const NMERR_ACCESSDENIED_NO:int		= -7;		//调用权限错误
		public static const NMERR_ACCESSDENIED_MSG:String	= "调用权限错误";
		
		public static const NMERR_SOCKETERR_NO:int		= -255;		//网络传输错误
		public static const NMERR_SOCKETERR_MSG:String	= "网络传输错误";

		private static var s_Instance:RPC = null;
		private static var m_nSessionIdx:uint = 0;
		
		private var m_szSession:SpLib.std.list = new SpLib.std.list;
		
		private var m_sNameSpace:String;
		
		public static function GetInstance():RPC
		{
			var name:String = "SpLib.Net.RPC";
			if(s_Instance==null)
			{
				s_Instance = SingletonFactory.Get(name) as SpLib.Net.RPC;
				if(s_Instance==null)
				{			
					s_Instance = new SpLib.Net.RPC();
					SingletonFactory.Reg(name,s_Instance);
				}
			}
			
			return s_Instance;
		}
		
		public function RPC()
		{
			if(SpLib.Net.RPC.s_Instance!=null)
			{
				throw new Error("RPC is Singleton");
			}
			
			if(SingletonFactory.Get("SpLib.Net.RPC")!=null)
			{
				throw new Error("RPC is Singleton");
			}
			
			SpLib.Com.Com.AddUpdateFunc(OnTimer);
		}
		
		private function OnTimer():void
		{
			//判断超时情况
			var now:uint = getTimer();
			
			var node:SpLib.std.iterator = m_szSession.begin();
			while(node!=null)
			{
				var session:Session = node.m_Data as Session;
				
				if(session.IsTimeOut(now))
				{
					if(session.m_bCallBack)
					{
						if(session.m_pLoopMsg!=null)
						{
							SpLib.Net.NetMsgMgr.SendMsg(session.m_pLoopMsg);
							session.m_CallTime = getTimer();
							SpLib.Tools.Tr.Call(session.m_sInfo);
						}
						
						node = node.next;
					}
					else
					{
						session.RetErr(NMERR_TIMEOUT_NO,NMERR_TIMEOUT_MSG);
						node = m_szSession.erase(node);
					}
				}
				else
				{
					node = node.next;
				}
			}
		}

		/**
		 * 设置服务默认命名空间
		 */
		public static function SetNameSpace(space:String):void
		{
			GetInstance().m_sNameSpace = space;
		}
		
		/**
		 * 获取服务默认命名空间
		 */
		public static function GetNameSpace():String
		{
			return GetInstance().m_sNameSpace;
		}
		
		/**
		 * 生成服务名全称，自动判断是否添加默认命名空间
		 */
		public static function MakeSName(name:String):String
		{
			//有显式namespace
			var pos:int = name.search("::");
			if(pos>0) return name;
			
			//无显式
			var space:String = GetNameSpace();
			if(space.length==0)
				return "Golbal::"+name;
	
			return space + "::" + name;
		}
		
		/**
		 * 标准RPC调用，等待返回，请自行注册
		 * 
		 * 将会广播如下消息
		 * SpLibEvent.RPC_RETOK
		 * SpLibEvent.RPC_RETERR
		 */
		public static function Call(sname:String,eve:String,agv:Array,okfunc:Function,errfunc:Function,outtime:int=DEF_OUTTIME,extagv:Array=null):void
		{
			sname = MakeSName(sname);
			
			var session:Session = new Session(++m_nSessionIdx,sname,eve,outtime,extagv);
			session.m_OkFunc = okfunc;
			session.m_ErrFunc = errfunc;
			GetInstance().m_szSession.push_back(session);
			
			var msg:SpLib.Net.NetMsg = new SpLib.Net.NetMsg();
			msg.m_nMsgtype = NetMsg.TYPE_RPC;
			msg.m_sEveName = eve;
			msg.m_sSName = sname;
			msg.m_Value = agv;
			msg.m_Address.m_nObjIDL = session.m_nIdx;
			SpLib.Net.NetMsgMgr.SendMsg(msg);
			
			SpLib.Tools.Tr.Call("Call " + sname + " " + eve + ":" + SpLib.Com.Com.Array2Str(agv));
		}
		
		/**
		 * 标准RPC调用，不等待返回
		 * 
		 */
		public static function Send(sname:String,eve:String,agv:Array):void
		{
			sname = MakeSName(sname);
			
			var msg:SpLib.Net.NetMsg = new SpLib.Net.NetMsg();
			msg.m_nMsgtype = NetMsg.TYPE_RPC;
			msg.m_sEveName = eve;
			msg.m_sSName = sname;
			msg.m_Value = agv;
			SpLib.Net.NetMsgMgr.SendMsg(msg);
			
			SpLib.Tools.Tr.Call("Send " + sname + " " + eve + ":" + SpLib.Com.Com.Array2Str(agv));
		}
		
		/**
		 * 注册RPC回调，用户服务器端主动消息推送，请自行注册
		 * 
		 * 将会广播如下消息
		 * SpLibEvent.RPC_RECALL
		 */
		public static function RegCallBack(sname:String,eve:String,agv:Array,func:Function,loop:Boolean = false,loopdelay:int=0):void
		{
			sname = MakeSName(sname);
			
			var session:Session = new Session(++m_nSessionIdx,sname,eve,loopdelay,null);
			session.m_bCallBack = true;
			session.m_OkFunc = func;
			
			GetInstance().m_szSession.push_back(session);
			
			var msg:SpLib.Net.NetMsg = new SpLib.Net.NetMsg();
			msg.m_nMsgtype = NetMsg.TYPE_RPC;
			msg.m_sEveName = eve;
			msg.m_sSName = sname;
			msg.m_Value = agv;
			msg.m_Address.m_nObjIDL = session.m_nIdx;
			
			if(loop)
			{
				session.m_pLoopMsg = msg;
				session.m_sInfo = "RegCallBack " + session.m_sSName + " " + session.m_sEvename + ":" + SpLib.Com.Com.Array2Str(agv);
			}
			
			SpLib.Net.NetMsgMgr.SendMsg(msg);
			SpLib.Tools.Tr.Call(session.m_sInfo);
		}
		
		/**
		 * 注销RPC回调
		 * 
		 */
		public static function UnRegCallBack(sname:String,eve:String,agv:Array,func:Function):void
		{
			sname = MakeSName(sname);
			
			var iter:SpLib.std.iterator = GetInstance().m_szSession.begin();
			while(iter!=null)
			{
				var session:Session = iter.m_Data as Session;
				
				if(session.m_bCallBack && 
					session.m_sSName==sname && 
					session.m_sEvename==eve && 
					session.m_OkFunc===func)
				{
					session.Clear();
					iter = GetInstance().m_szSession.erase(iter);
				}
				else
				{
					iter = iter.next;
				}
			}
		}
		
		public static function MsgRet(msg:NetMsg):void
		{
			var node:SpLib.std.iterator = GetInstance().m_szSession.begin();
			while(node!=null)
			{
				var session:Session = node.m_Data as Session;
				
				if(session.m_nIdx==msg.m_Address.m_nObjIDL)
				{
					//CallBack
					if(msg.m_nMsgtype==NetMsg.TYPE_SERVERPUSH)
					{
						SpLib.Tools.Tr.CallRet("CallBack " + session.m_sSName + " " + session.m_sEvename + ":" + SpLib.Com.Com.Array2Str(msg.m_Value));
						
						session.CallBack(msg.m_nEveID,msg.m_Value);
						return;
					}
					
					//RPC返回
					if(msg.m_nMsgtype==NetMsg.TYPE_RPC_RESP)
					{
						GetInstance().m_szSession.erase(node);
						
						SpLib.Tools.Tr.CallRet("Ret " + session.m_sSName + " " + session.m_sEvename + ":" + SpLib.Com.Com.Array2Str(msg.m_Value));
						
						if(msg.m_nEveID==1)
						{
							session.RetOk(msg.m_Value);
						}
						else
						{
							session.RetErr(msg.m_Value[0] as int,msg.m_Value[1] as String);
						}
						
						session.Clear();
						return;
					}
					
					return;
				}
				else
				{
					node = node.next;
				}
			}
		}
	}
}

import flash.events.EventDispatcher;
import SpLib.Com.SpLibEvent;
import flash.utils.getTimer;
import flash.events.Event;
import SpLib.Net.NetMsg;

class Session
{
	public var m_sSName:String;
	public var m_sEvename:String;
	
	public var m_nIdx:uint = 0;

	public var m_ExtAgv:Array = new Array;
	public var m_nOutTime:int;
	public var m_CallTime:uint;
	
	public var m_pLoopMsg:SpLib.Net.NetMsg = null;
	public var m_bCallBack:Boolean = false;
	
	public var m_sInfo:String;
	
	public var m_OkFunc:Function = null;
	public var m_ErrFunc:Function = null;
	
	public function Session(idx:uint,sname:String,evename:String,out:int,extagv:Array)
	{
		m_nIdx = idx;
		
		m_sSName = sname;
		m_sEvename = evename;
		
		m_nOutTime = out;
		m_ExtAgv = extagv;
		m_CallTime = getTimer();
	}
	
	public function IsTimeOut(now:uint):Boolean
	{
		if(m_nOutTime==-1)
		{
			return false;
		}
		
		var time:int = now - m_CallTime;
		if(time>m_nOutTime)
		{
			return true;
		}
		
		return false;
	}
	
	public function RetOk(retagv:Array):void
	{
		var event:SpLibEvent = new SpLibEvent(SpLibEvent.RPC_RETOK,"",retagv);
		event.m_ExtAgv = m_ExtAgv;
		if(m_OkFunc!=null)
		{
			m_OkFunc(event);
		}
	}
	
	public function RetErr(errflag:int,errmsg:String):void
	{
		var event:SpLibEvent = new SpLibEvent(SpLibEvent.RPC_RETERR,"");
		event.m_ExtAgv = m_ExtAgv;
		event.m_nErrFlag = errflag;
		event.m_sErrMsg = errmsg;
		
		if(m_ErrFunc!=null)
		{
			m_ErrFunc(event);
		}
	}
	
	public function CallBack(eveid:uint,retagv:Array):void
	{
		var event:SpLibEvent = new SpLibEvent(SpLibEvent.RPC_RECALL,"");
		event.m_Eve.m_nEveID = eveid;
		
		if(m_OkFunc!=null)
		{
			m_OkFunc(event);
		}
	}
	
	public function Clear():void
	{
		m_OkFunc = null;
		m_ErrFunc = null;
	}
}